package org.sja.log2traffic;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import org.joda.time.DateTime;
import org.sja.log2traffic.dataStructures.ConfigurationEntity;

public class DemoTrafficGenerator {

	private IFileOperations fileOperations;
	private IDateTimeFormatter dateTimeFormatter;
	private ConfigurationManipulator configurationManipulator;
	private Random random = new Random();
	final private int amountOfServices = 3;

	public static void main(String[] args){
		
		DemoTrafficGenerator instance = new DemoTrafficGenerator();
		instance.generateFromPropertyFile(args[0]);

	}
	
	private void generateFromPropertyFile(String propertiesFilename){

		File configFile = new File(propertiesFilename);
		if (!(configFile.isFile())){
			throw new RuntimeException("File not exist: " + propertiesFilename);
		}
		
		ConfigurationEntity configurationForMonths = configurationManipulator.getConfigurationForMonthsReport(propertiesFilename);
		
		generateFiles(7,configurationForMonths.getInputDirectories()[0], configurationForMonths.getInputDirectories()[1], null, null, null);
		
	}
	
	
	public DemoTrafficGenerator(){
		this.fileOperations = LogAnalyzerFactory.getInstance().provideFileOperations();
		this.dateTimeFormatter = LogAnalyzerFactory.getInstance().provideDateTimeFormatter();
		this.configurationManipulator = LogAnalyzerFactory.getInstance().provideConfigurationManipulator();
	}

	public void generateFiles(Integer amountOfMonths, File dir1, File dir2, Map<String, Map<DateTime, Long>> agregatedDays, Map<String, Map<DateTime, Long>> agregatedHours, Map<String, Map<DateTime, Long>> agregatedMinutes) {

		DateTime from = new DateTime().minusMonths(amountOfMonths);
		DateTime max = new DateTime();

		fileOperations.deleteDirectory(dir1);
		fileOperations.deleteDirectory(dir2);
		dir1.mkdirs();
		dir2.mkdirs();
		DateTime fromFloor = from.dayOfMonth().roundFloorCopy();

		for (DateTime i = fromFloor; i.isBefore(max); i = i.plusDays(1)) {
			DateTime nextValue = new DateTime(i);
			DateTime to = new DateTime(i.plusDays(1));
			if (to.isAfter(max)) {
				to = new DateTime(max);
			}
			File file1 = new File(dir1.getAbsolutePath() + '/' + "accessLogFile1_" + dateTimeFormatter.longToStringCondendensed(nextValue.getMillis()) + ".log");
			File file2 = new File(dir2.getAbsolutePath() + '/' + "accessLogFile2_" + dateTimeFormatter.longToStringCondendensed(nextValue.getMillis()) + ".log");
			PrintWriter printWriter1 = null, printWriter2 = null;
			try {
				printWriter1 = new PrintWriter(new FileWriter(file1));
				printWriter2 = new PrintWriter(new FileWriter(file2));
				while (nextValue.isBefore(to)) {
					int serviceId = random(1, amountOfServices + 1);
					int responseTime = random(50, 2000);
					DateTime afterResponseTime = nextValue.plusMillis(responseTime);
					if (!afterResponseTime.isBefore(to)){
						break;
					}
					if (random(0, 2) == 0) {
						writeLine(printWriter1, nextValue, responseTime, afterResponseTime, serviceId);
					} else {
						writeLine(printWriter2, nextValue, responseTime, afterResponseTime, serviceId);
					}
					nextValue = afterResponseTime;
					if (agregatedDays != null) {
						addToStruct(getStruct(agregatedDays, serviceId), nextValue.dayOfMonth().roundFloorCopy());
					}
					if (agregatedHours != null) {
						addToStruct(getStruct(agregatedHours, serviceId), nextValue.hourOfDay().roundFloorCopy());
					}
					if (agregatedMinutes != null) {
						addToStruct(getStruct(agregatedMinutes, serviceId), nextValue.minuteOfHour().roundFloorCopy());
					}

					int nextTimePeriod = random(300, 40000);
					nextValue = nextValue.plusMillis(nextTimePeriod);
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (printWriter1 != null) {
					printWriter1.close();
				}
				if (printWriter2 != null) {
					printWriter2.close();
				}
			}
		}
	}

	private int random(int from, int to) {
		int range = to - from;
		int offset = (int) (Math.abs(random.nextLong()) % range);
		return from + offset;
	}

	private void addToStruct(Map<DateTime, Long> struct, DateTime key) {
		Long value = struct.get(key);
		if (value == null) {
			value = 0L;
		}
		struct.put(key, value + 1);
	}

	private void writeLine(PrintWriter printWriter, DateTime timeStamp, int responseTime, DateTime afterResponseTime, Integer serviceId) {

		String timeStampStr = dateTimeFormatter.longToStringWithMilis(timeStamp.getMillis());
		String threadNumber = String.valueOf(random(1, 11));
		printWriter.print(timeStampStr);
		printWriter.print(" [THREAD-" + threadNumber + ']');
		printWriter.print(" myApp/servlet_" + serviceId + " was called successfully");
		printWriter.println();

		String timeStampStr2 = dateTimeFormatter.longToStringWithMilis(afterResponseTime.getMillis());
		printWriter.print(timeStampStr2);
		printWriter.print(" [THREAD-" + threadNumber + ']');
		printWriter.print(" RESPONSE_TIME:" + responseTime);
		printWriter.println();
		
	}

	private Map<DateTime, Long> getStruct(Map<String, Map<DateTime, Long>> struct, Integer serviceId) {
		String key = "Feature_" + serviceId;
		Map<DateTime, Long> result = struct.get(key);
		if (result == null) {
			result = new HashMap<DateTime, Long>();
			struct.put(key, result);
		}
		return result;
	}

	public int getAmountOfServices() {
		return amountOfServices;
	}
	

}
